﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Collections;

namespace DataMining
{
    public class Entropy
    {
        public int[] statNum; //训练样例集统计结果:0,unacc;1,acc;2,good;3,vgood
        string[] resultString = Form1.strResult;//训练集结果string
        public double EntropyValue = 0;
        private int mTotal = 0;
        private string mTargetAttribute = "result";

        public Entropy()
        {
            statNum = new int[resultString.Length];
        }

        public void getEntropy(DataTable samples)
        {
            countTotalClass(samples, ref statNum, resultString);
            EntropyValue = calcEntropy(statNum);

        }
      
        /// <summary>
        /// 统计各个类所包含的样例数
        /// </summary>
        /// <param name="samples">表信息</param>
        /// <param name="iarr">统计值</param>
        /// <param name="sarr">统计的名称</param>
        public void countTotalClass(DataTable samples, ref int[] iarr, string[] resultString)
        {
            // 初始化
            for (int i = 0; i < resultString.Length; i++)
            {
                iarr[i] = 0;
            }
            foreach (DataRow aRow in samples.Rows)
            {
                for (int n = 0; n < resultString.Length; n++)
                    {
                        if ((String)aRow[mTargetAttribute] == resultString[n])
                            iarr[n]++;

                    }
                
            }

        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="positives"></param>
        /// <param name="negatives"></param>
        /// <returns></returns>
        //public double calcEntropy(int unacc,int acc,int good,int vgood)
        //{
        //    double entropy = 0;
        //    double total = unacc + acc + good + vgood;
        //    double p = 0;
        //    if (unacc != 0)
        //    {
        //        p = unacc / total;
        //        entropy += -p * Math.Log(p, 2);
        //    }
        //    if (acc != 0)
        //    {
        //        p = acc / total;
        //        entropy += -p * Math.Log(p, 2);
        //    }
        //    if (good != 0)
        //    {
        //        p = good / total;
        //        entropy += -p * Math.Log(p, 2);
        //    }
        //    if (vgood != 0)
        //    {
        //        p = vgood / total;
        //        entropy += -p * Math.Log(p, 2);
        //    }

        //    return entropy;
        //}

        //属性熵公式
        public double calcEntropy(int[] statNum)
        {
            double entropy = 0;
            double total = 0;
            double p = 0;
            for (int i = 0; i < statNum.Length; i++)
            {
                total += statNum[i];
            }
            for (int i = 0; i < statNum.Length; i++)
            {
                if (statNum[i] != 0)
                {
                    p = statNum[i] / total;
                    entropy += -p * Math.Log(p, 2);
                }
            }
            
            return entropy;
        }
        

        /// <summary>
        ///
        /// </summary>
        /// <param name="samples">DataTable com as amostras</param>
        /// <param name="attribute">Atributo a ser pesquisado</param>
        /// <param name="value">valor permitido para o atributo</param>
        /// <param name="positives">Conter?o nro de todos os atributos com o valor determinado com resultado positivo</param>
        /// <param name="negatives">Conter?o nro de todos os atributos com o valor determinado com resultado negativo</param>
        public void getValuesToAttribute(DataTable samples, Attribute attribute, string value, out int unacc, out int acc, out int good, out int vgood)
        {
            unacc = 0;
            acc = 0;
            good = 0;
            vgood = 0;

            foreach (DataRow aRow in samples.Rows)
            {
                if (((string)aRow[attribute.AttributeName] == value))
                {
                    if ((String)aRow[mTargetAttribute] == "B")
                        unacc++;
                    else if (aRow[mTargetAttribute].ToString() == "A")
                        acc++;
                    else if (aRow[mTargetAttribute].ToString() == "R")
                        good++;
                    else if (aRow[mTargetAttribute].ToString() == "L")
                        vgood++;
                }
            }
        }

        public void getValuesToAttribute(DataTable samples, Attribute attribute, string value,  ref int[] statNum, string[] resultString)
        {
            for(int i=0;i<statNum.Length;i++)
            {
                statNum[i]=0;
            }
            foreach (DataRow aRow in samples.Rows)
            {
                for (int i = 0; i < resultString.Length; i++)
                {
                    if (((string)aRow[attribute.AttributeName] == value))
                    {
                        if ((String)aRow[mTargetAttribute] == resultString[i])
                            statNum[i]++;
                    }
                }
            }
                
        }
        /// <summary>
        /// Calcula o ganho de um atributo
        /// </summary>
        /// <param name="attribute">Atributo a ser calculado</param>
        /// <returns>O ganho do atributo</returns>
        public double gain(DataTable samples, Attribute attribute)
        {

            mTotal = samples.Rows.Count;

            string[] values = attribute.values;
            double sum = 0.0;

            for (int i = 0; i < values.Length; i++)
            {
                int[] stat_Num = statNum;
                for (int j = 0; j < stat_Num.Length;j++ )
                {
                    stat_Num[j] = 0;
                }
                getValuesToAttribute(samples, attribute, values[i], ref stat_Num, resultString);
                double entropy = calcEntropy(statNum);
                int total = 0;
                for (int k = 0; k < stat_Num.Length; k++)
                {
                    total += stat_Num[k];
                }
                    sum += -(double)(total) / mTotal * entropy;
            }
            return EntropyValue + sum;
        }

        /// <summary>
        /// 获得targetAttribute属性下的所有属性值
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="targetAttribute"></param>
        /// <returns></returns>
        private ArrayList getDistinctValues(DataTable samples, string targetAttribute)
        {
            ArrayList distinctValues = new ArrayList(samples.Rows.Count);

            foreach (DataRow row in samples.Rows)
            {
                if (distinctValues.IndexOf(row[targetAttribute]) == -1)
                    distinctValues.Add(row[targetAttribute]);
            }

            return distinctValues;
        }

        /// <summary>
        /// 计算分裂信息
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="attribute"></param>
        /// <returns></returns>
        public double splitInfo(DataTable samples, string attribute)
        {
            ArrayList values = getDistinctValues(samples, attribute);
            for (int i = 0; i < values.Count; i++)
            {
                if (values[i] == null || values[i] == "")
                    values.RemoveAt(i);
            }

            int[] count = new int[values.Count];

            for (int i = 0; i < values.Count; i++)
            {
                foreach (DataRow aRow in samples.Rows)
                {
                    //if ((String)aRow[mTargetAttribute] == values[i])
                    //    count[i]++;
                    if ((String)aRow[attribute] == values[i])
                        count[i]++;
                }
            }

            double entropy = 0;
            double total = samples.Rows.Count;
            double p = 0;

            for (int i = 0; i < values.Count; i++)
            {
                if (count[i] != 0)
                {
                    p = count[i] / total;
                    entropy += -p * Math.Log(p, 2);
                }
            }

            return entropy;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="attribute"></param>
        /// <returns></returns>
        public double gainRatio(DataTable samples, Attribute attribute)
        {
            double splitInfoA = this.splitInfo(samples,attribute.AttributeName);   //按A划分的信息增益
            double gainA=gain(samples,attribute);               //信息增益
            double gainRatioA;          //信息增益率

            gainRatioA = gainA / splitInfoA;

            return gainRatioA;


        }
    }
}
